/**
 * 常用工具类
 */
package com.apache.client.common;

import com.apache.exception.BusinessException;
import com.apache.tools.LogUtil;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;

/**
 * 支持图片缩放、图片水印、文字水印、图片剪切 图片支持jpg、bmp、jpeg格式
 *
 * @author ysliu
 */
public final class ImageUtil {

    private static ImageUtil instance;

    private ImageUtil() {
        //ImageUtil
    }

    public static ImageUtil getInstance() {
        if (null == instance) {
            instance = new ImageUtil();
        }
        return instance;
    }

    /**
     * 图片水印
     *
     * @param pressImg 水印图片
     * @param targetImg 目标图片
     * @param x 修正值 默认在中间
     * @param y 修正值 默认在中间
     * @param apache 透明度
     */
    public final void pressImage(String pressImg, String targetImg, int x, int y, float apache)
            throws IOException {
        File img = new File(targetImg);
        Image src = ImageIO.read(img);
        int wideth = src.getWidth(null);
        int height = src.getHeight(null);
        BufferedImage image = new BufferedImage(wideth, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        g.drawImage(src, 0, 0, wideth, height, null);
        // 水印文件
        Image src_biao = ImageIO.read(new File(pressImg));
        int wideth_biao = src_biao.getWidth(null);
        int height_biao = src_biao.getHeight(null);
        //调整x、y的坐标
        if (x > (wideth - wideth_biao) / 2) {
            x = (wideth - wideth_biao) / 2;
        } else if (x < -((wideth - wideth_biao) / 2)) {
            x = -(wideth - wideth_biao) / 2;
        }
        if (y > (height - height_biao) / 2) {
            y = (height - height_biao) / 2;
        } else if (y < -((height - height_biao) / 2)) {
            y = -(height - height_biao) / 2;
        }
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, apache));
        g.drawImage(src_biao, (wideth - wideth_biao) / 2 + x, (height - height_biao) / 2 + y,
                wideth_biao, height_biao, null);
        // 水印文件结束
        g.dispose();
        ImageIO.write((BufferedImage) image, "jpg", img);
    }

    /**
     * 文字水印
     *
     * @param pressText 水印文字
     * @param targetImg 目标图片
     * @param fontName 字体名称
     * @param fontStyle 字体样式
     * @param color 字体颜色
     * @param fontSize 字体大小
     * @param x 修正值
     * @param y 修正值
     * @param apache 透明度
     */
    public void pressText(String pressText, String targetImg, String fontName, int fontStyle,
            Color color, int fontSize, int x, int y, float apache) {
        try {
            File img = new File(targetImg);
            Image src = ImageIO.read(img);
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));
            int widthInt = (width - (getLength(pressText) * fontSize));
            //调整x、y的坐标
            if (x > widthInt / 2) {
                x = widthInt / 2;
            } else if (x < -widthInt / 2) {
                x = -widthInt / 2;
            }
            if (y > (height - fontSize) / 2) {
                y = (height - fontSize) / 2;
            } else if (y < -((height - fontSize) / 2)) {
                y = -((height - fontSize) / 2);
            }
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, apache));
            g.drawString(pressText, (width - (getLength(pressText) * fontSize)) / 2 + x,
                    (height - fontSize) / 2 + y);
            g.dispose();
            ImageIO.write((BufferedImage) image, "jpg", img);
        } catch (Exception e) {
            LogUtil.error("" + e);
        }
    }

    /**
     * 缩放
     *
     * @param source 图片
     * @param targetW 宽
     * @param targetH 高
     * @param scale 是否是等比例缩放，true是，false非
     * @return
     */
    private BufferedImage resizeImage(BufferedImage source, int targetW, int targetH,
            boolean scale) {
        // targetW，targetH分别表示目标长和宽
        int type = source.getType();
        BufferedImage target = null;
        double sx = (double) targetW / source.getWidth();
        double sy = (double) targetH / source.getHeight();
        //这里想实现在targetW，targetH范围内实现等比缩放。如果不需要等比缩放
        //则将下面的if else语句注释即可
        if (scale) {
            if (sx > sy) {
                targetH = (int) (sx * source.getHeight());
                sy = sx;
            } else {
                targetW = (int) (sy * source.getWidth());
                sx = sy;
            }
        }
        if (type == BufferedImage.TYPE_CUSTOM) { //handmade  //not pic
            ColorModel cm = source.getColorModel();
            WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);
            boolean apachePremultiplied = cm.isAlphaPremultiplied();
            target = new BufferedImage(cm, raster, apachePremultiplied, null);
        } else {
            target = new BufferedImage(targetW, targetH, type);
        }
        Graphics2D g = target.createGraphics();
        //smoother than exlax:
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); //old
        //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);    //没有上一个效果好
        g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
        g.dispose();
        return target;
    }

    /**
     * 按照给定的宽和高进行缩放
     * <b>非等比例缩放</b>
     *
     * @param source
     * @param targetW
     * @param targetH
     * @return
     */
    private BufferedImage resizeImage(BufferedImage source, int targetW, int targetH) {
        return resizeImage(source, targetW, targetH, false);
    }

    /**
     * 按宽度等比例缩放
     *
     * @param source
     * @param targetW
     * @return
     */
    private BufferedImage resizeImageW(BufferedImage source, int targetW) {
        return resizeImage(source, targetW, 0, true);
    }

    /**
     * 按长度等比例缩放
     *
     * @param source
     * @param targetH
     * @return
     */
    private BufferedImage resizeImageH(BufferedImage source, int targetH) {
        return resizeImage(source, 0, targetH, true);
    }

    /**
     * 缩放，支持等比例缩放
     *
     * @param filePath 图片路径
     * @param width 宽
     * @param hight 高
     */
    public void resize(String filePath, int width, int hight) throws BusinessException {
        try {
            BufferedImage srcImage;
            // String ex =
            // fromFileStr.substring(fromFileStr.indexOf("."),fromFileStr.length())
            String imgType = "JPEG";
            if (filePath.toLowerCase().endsWith(".png")) {
                imgType = "PNG";
            }
            File file = new File(filePath);
            srcImage = ImageIO.read(file);
            if (width > 0 && hight > 0) {
                srcImage = resizeImage(srcImage, width, hight);
            } else if (width > 0) {
                srcImage = resizeImageW(srcImage, width);
            } else if (hight > 0) {
                srcImage = resizeImageH(srcImage, hight);
            } else {
                return;
            }
            ImageIO.write(srcImage, imgType, file);
        } catch (Exception e) {
            LogUtil.error("" + e);
            throw new BusinessException("pic resize error at read or write!!");
        }
    }

    /**
     * 根据指定的位置和大小对图片进行裁剪
     *
     * @param filepath 图片路径
     * @param x 要截取图片的起始坐标x点
     * @param y 要截取图片的起始坐标y点
     * @param width 要截取图片的宽度
     * @param height 要截取图片的高度
     * @exception IOException 读写异常
     */
    public void cut(String filepath, int x, int y, int width, int height) throws IOException {

        FileInputStream is = null;
        ImageInputStream iis = null;

        try {
            // 读取图片文件
            is = new FileInputStream(filepath);

            /*
             * 返回包含所有当前已注册 ImageReader 的 Iterator，这些 ImageReader 声称能够解码指定格式。
             * 参数：formatName - 包含非正式格式名称 . （例如 "jpeg" 或 "tiff"）等 。
             */
            Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName("jpg");
            ImageReader reader = it.next();
            // 获取图片流
            iis = ImageIO.createImageInputStream(is);

            /*
             * <p>iis:读取源.true:只向前搜索 </p>.将它标记为 ‘只向前搜索’。
             * 此设置意味着包含在输入源中的图像将只按顺序读取，可能允许 reader 避免缓存包含与以前已经读取的图像关联的数据的那些输入部分。
             */
            reader.setInput(iis, true);

            /*
             * 获取原始图片的大小
             */
            int originWidth = reader.getWidth(0);
            int originHeight = reader.getHeight(0);

            /*
             * <p>描述如何对流进行解码的类<p>.用于指定如何在输入时从 Java Image I/O
             * 框架的上下文中的流转换一幅图像或一组图像。用于特定图像格式的插件 将从其 ImageReader 实现的
             * getDefaultReadParam 方法中返回 ImageReadParam 的实例。
             */
            ImageReadParam param = reader.getDefaultReadParam();

            /*
             * 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域，通过 Rectangle 对象
             * 的左上顶点的坐标（x，y）、宽度和高度可以定义这个区域。 宽度和高度不能超过原始图片，否则只能以原图片大小截取。
             */
            if (width >= originWidth) {
                width = originWidth;
            }
            if (height >= originHeight) {
                height = originHeight;
            }
            if (x >= width) {
                x = 0;
            }
            if (y >= height) {
                y = 0;
            }
            Rectangle rect = new Rectangle(x, y, width, height);

            // 提供一个 BufferedImage，将其用作解码像素数据的目标。
            param.setSourceRegion(rect);

            /*
             * 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象，并将 它作为一个完整的
             * BufferedImage 返回。
             */
            BufferedImage bi = reader.read(0, param);

            // 保存新图片
            ImageIO.write(bi, "jpg", new File(filepath));
        } finally {
            if (is != null)
                is.close();
            if (iis != null)
                iis.close();
        }

    }

    private int getLength(String text) {
        int length = 0;
        for (int i = 0; i < text.length(); i++) {
            if (String.valueOf(text.charAt(i) + "").getBytes().length > 1) {
                length += 2;
            } else {
                length += 1;
            }
        }
        return length / 2;
    }
}